/**
 * This file is Copyright © 2008 Software Craftsmen Limited. All Rights Reserved.
 */
package com.softwarecraftsmen.orogeny.execution;

import com.softwarecraftsmen.orogeny.buildLogs.Indentation;
import com.softwarecraftsmen.orogeny.buildLogs.IndentingBuildLog;
import com.softwarecraftsmen.orogeny.buildLogs.Verbosity;
import org.jetbrains.annotations.NotNull;

import static java.lang.String.format;
import java.util.ArrayList;
import java.util.List;
import static java.util.Locale.UK;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class RecordingIndentingBuildLog implements RecordingBuildLog
{
	private final List<Arguments<?, ?>> record;
	private final transient Lock readLock;
	private final transient Lock writeLock;

	public RecordingIndentingBuildLog()
	{
		record = new ArrayList<>();
		final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);
		readLock = readWriteLock.readLock();
		writeLock = readWriteLock.writeLock();
	}

	public void writeMessage(final @NotNull Indentation indentation, final @NotNull String message)
	{
		writeLock.lock();
		try
		{
			record.add(new Arguments<Indentation, String>(indentation, message)
			{
				public void writeMessage(final @NotNull IndentingBuildLog indentingBuildLog)
				{
					indentingBuildLog.writeMessage(a, b);
				}
			});
		}
		finally
		{
			writeLock.unlock();
		}
	}

	public void writeMessage(final @NotNull Indentation indentation, final @NotNull SuccessOrFailure successOrFailure)
	{
		writeLock.lock();
		try
		{
			record.add(new Arguments<Indentation, SuccessOrFailure>(indentation, successOrFailure)
			{
				public void writeMessage(final @NotNull IndentingBuildLog indentingBuildLog)
				{
					indentingBuildLog.writeMessage(a, b);
				}
			});
		}
		finally
		{
			writeLock.unlock();
		}
	}

	public void writeMessage(final @NotNull Verbosity verbosity, final @NotNull String message)
	{
		writeLock.lock();
		try
		{
			record.add(new Arguments<Verbosity, String>(verbosity, message)
			{
				public void writeMessage(final @NotNull IndentingBuildLog indentingBuildLog)
				{
					indentingBuildLog.writeMessage(a, b);
				}
			});
		}
		finally
		{
			writeLock.unlock();
		}
	}

	public void writeMessage(final @NotNull IndentingBuildLog indentingBuildLog)
	{
		readLock.lock();
		try
		{
			for (Arguments<?, ?> arguments : record)
			{
				arguments.writeMessage(indentingBuildLog);
			}
		}
		finally
		{
			readLock.unlock();
		}
	}

	private static abstract class Arguments<A, B>
	{
		protected final A a;
		protected final B b;

		public Arguments(final @NotNull A a, final @NotNull B b)
		{
			this.a = a;
			this.b = b;
		}

		public abstract void writeMessage(final @NotNull IndentingBuildLog indentingBuildLog);

		@NotNull
		public String toString()
		{
			return format(UK, "%1$s: %2$s", a, b);
		}
	}
}
